#--------------------------------------------------------#
# Libraries #
#--------------------------------------------------------#
set.seed(2020)
#library(ranger)
#library(dplyr)
#library(caret)
library(tidymodels)
library(usemodels)
#--------------------------------------------------------#
# Data #
#--------------------------------------------------------#
FS1_T0_Cluster_Assignment <-read.csv("D:\\DKE\\Thesis_related\\Implementation\\Hyper_tuning_results\\FS1_T0_Cluster_Assignment.csv")
#Convert Cluster ID from int to factor
FS1_T0_Cluster_Assignment<- FS1_T0_Cluster_Assignment %>% mutate(Cluster_ID=as.factor(Cluster_ID))
#--------------------------------------------------------#
There were 12 warnings (use warnings() to see them)
# Slitting Data #
#--------------------------------------------------------#
set.seed(123)
#ikea_split
FS1_T0_Cluster_split <- initial_split(FS1_T0_Cluster_Assignment, strata = Cluster_ID)
FS1_T0_Cluster_train <- training(FS1_T0_Cluster_split)
FS1_T0_Cluster_test <- testing(FS1_T0_Cluster_split)
set.seed(234)
FS1_T0_Cluster_folds <- bootstraps(FS1_T0_Cluster_train, strata = Cluster_ID)
FS1_T0_Cluster_folds
# Bootstrap sampling using stratification
#library(usemodels)
use_ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train)
ranger_recipe <-
recipe(formula = Cluster_ID ~ ., data = FS1_T0_Cluster_train)
ranger_spec <-
rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>%
set_mode("classification") %>%
set_engine("ranger")
ranger_workflow <-
workflow() %>%
add_recipe(ranger_recipe) %>%
add_model(ranger_spec)
set.seed(68322)
ranger_tune <-
tune_grid(ranger_workflow, resamples = [31mstop("add your rsample object")[39m, grid = [31mstop("add number of candidate points")[39m)
#--------------------------------------------------------#
# Start tuning #
#--------------------------------------------------------#
ranger_recipe <-
recipe(formula = Cluster_ID ~ ., data = FS1_T0_Cluster_train) %>% update_role(Instance_ID, new_role = "ID")
ranger_spec <-
rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>%
set_mode("classification") %>%
set_engine("ranger")
ranger_workflow <-
workflow() %>%
add_recipe(ranger_recipe) %>%
add_model(ranger_spec)
ranger_folds <- vfold_cv(FS1_T0_Cluster_train)
set.seed(68322)
ranger_tune <-
tune_grid(ranger_workflow, resamples = ranger_folds, grid = 20)
ranger_tune %>% collect_metrics()
#--------------------------------------------------------#
There were 14 warnings (use warnings() to see them)
# graphical results #
#--------------------------------------------------------#
ranger_tune %>%
collect_metrics() %>%
filter(.metric == "roc_auc") %>%
mutate(min_n = factor(min_n)) %>%
ggplot(aes(mtry, mean, color = min_n)) +
geom_line(alpha = 0.5, size = 1.5) +
geom_point() +
labs(y = "AUC")

# Specifying the mtry and min_n range to understand better
rf_grid_ranger <- grid_regular(
mtry(range = c(10, 30)),
min_n(range = c(2, 8)),
levels = 5
)
rf_grid_ranger
doParallel::registerDoParallel()
ranger_tune <-
tune_grid(ranger_workflow, resamples = ranger_folds, grid = rf_grid_ranger)
show_best(ranger_tune, metric = "roc_auc")
show_best(ranger_tune, metric = "accuracy")
select_best(ranger_tune,metric = "roc_auc")
autoplot(ranger_tune)

#--------------------------------------------------------#
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
# graphical results #
#--------------------------------------------------------#
ranger_tune %>%
collect_metrics() %>%
filter(.metric == "roc_auc") %>%
mutate(min_n = factor(min_n)) %>%
ggplot(aes(mtry, mean, color = min_n)) +
geom_line(alpha = 0.5, size = 1.5) +
geom_point() +
labs(y = "AUC")

#--------------------------------------------------------#
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
# Show and select the best #
#--------------------------------------------------------#
show_best(ranger_tune, metric = "roc_auc")
show_best(ranger_tune, metric = "accuracy")
select_best(ranger_tune,metric = "roc_auc")
autoplot(ranger_tune)

#--------------------------------------------------------#
# finalize the model and workflow #
#--------------------------------------------------------#
final_rf <- ranger_workflow %>%
finalize_workflow(select_best(ranger_tune))
No value of `metric` was given; metric 'roc_auc' will be used.
final_rf
== Workflow ==============================================================================================================================
[3mPreprocessor:[23m Recipe
[3mModel:[23m rand_forest()
-- Preprocessor --------------------------------------------------------------------------------------------------------------------------
0 Recipe Steps
-- Model ---------------------------------------------------------------------------------------------------------------------------------
Random Forest Model Specification (classification)
Main Arguments:
mtry = 10
trees = 1000
min_n = 2
Computational engine: ranger
#--------------------------------------------------------#
# fit the best tuned parameter on the training data #
#--------------------------------------------------------#
FS1_T0_Cluster_fit <- last_fit(final_rf, FS1_T0_Cluster_split)
FS1_T0_Cluster_fit
# Resampling results
# Manual resampling
collect_metrics(FS1_T0_Cluster_fit)
library(vip)
imp_spec <- ranger_spec %>%
finalize_model(select_best(ranger_tune)) %>%
set_engine("ranger", importance = "permutation")
No value of `metric` was given; metric 'roc_auc' will be used.
workflow() %>%
add_recipe(ranger_recipe) %>%
add_model(imp_spec) %>%
fit(FS1_T0_Cluster_train) %>%
pull_workflow_fit() %>%
vip(aesthetics = list(alpha = 0.8, fill = "midnightblue"))

Ranger package
- Above tuned values used for ranger model.
- Ranger is a fast implementation of random forests (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data.
tidy_ranger_model <- ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train[,-1] , importance = "permutation",
local.importance = TRUE,mtry = 10,num.trees = 1000,classification = TRUE,
min.node.size = 2)
tidy_ranger_model
Ranger result
Call:
ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train[, -1], importance = "permutation", local.importance = TRUE, mtry = 10, num.trees = 1000, classification = TRUE, min.node.size = 2)
Type: Classification
Number of trees: 1000
Sample size: 1306
Number of independent variables: 44
Mtry: 10
Target node size: 2
Variable importance mode: permutation
Splitrule: gini
OOB prediction error: 26.49 %
Train accuracy
confusionMatrix(tidy_ranger_model$confusion.matrix)
Confusion Matrix and Statistics
predicted
true 1 2 3
1 268 114 8
2 75 479 57
3 2 90 213
Overall Statistics
Accuracy : 0.7351
95% CI : (0.7102, 0.7588)
No Information Rate : 0.523
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.5773
Mcnemar's Test P-Value : 0.0002662
Statistics by Class:
Class: 1 Class: 2 Class: 3
Sensitivity 0.7768 0.7013 0.7662
Specificity 0.8730 0.7881 0.9105
Pos Pred Value 0.6872 0.7840 0.6984
Neg Pred Value 0.9159 0.7065 0.9351
Prevalence 0.2642 0.5230 0.2129
Detection Rate 0.2052 0.3668 0.1631
Detection Prevalence 0.2986 0.4678 0.2335
Balanced Accuracy 0.8249 0.7447 0.8383
Test accuracy
tidy_ranger_pred.data <- predict(tidy_ranger_model, data = FS1_T0_Cluster_test)
table(FS1_T0_Cluster_test$Cluster_ID, tidy_ranger_pred.data$predictions)
1 2 3
1 86 44 1
2 30 161 13
3 0 43 59
tidy_ranger_cm<-confusionMatrix(table(FS1_T0_Cluster_test$Cluster_ID, tidy_ranger_pred.data$predictions, dnn = c("Reference", "Prediction")))
tidy_ranger_cm
Confusion Matrix and Statistics
Prediction
Reference 1 2 3
1 86 44 1
2 30 161 13
3 0 43 59
Overall Statistics
Accuracy : 0.7002
95% CI : (0.6549, 0.7428)
No Information Rate : 0.5675
P-Value [Acc > NIR] : 7.837e-09
Kappa : 0.5138
Mcnemar's Test P-Value : 0.000194
Statistics by Class:
Class: 1 Class: 2 Class: 3
Sensitivity 0.7414 0.6492 0.8082
Specificity 0.8598 0.7725 0.8819
Pos Pred Value 0.6565 0.7892 0.5784
Neg Pred Value 0.9020 0.6266 0.9582
Prevalence 0.2654 0.5675 0.1670
Detection Rate 0.1968 0.3684 0.1350
Detection Prevalence 0.2998 0.4668 0.2334
Balanced Accuracy 0.8006 0.7108 0.8450
tidy(tidy_ranger_cm)
Conclusion : Model accuracy
- Train Accuracy : 73.66%
- Test Accuracy : 70.02%
- Both are close by. Balanced model.
LS0tDQp0aXRsZTogIlRpZHkgbW9kZWxzIGFsb25nIHdpdGggcmFuZ2VyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgICAgICAgICBMaWJyYXJpZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0Kc2V0LnNlZWQoMjAyMCkNCiNsaWJyYXJ5KHJhbmdlcikNCiNsaWJyYXJ5KGRwbHlyKQ0KI2xpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHVzZW1vZGVscykNCmBgYA0KDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgICAgICAgICBEYXRhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KRlMxX1QwX0NsdXN0ZXJfQXNzaWdubWVudCA8LXJlYWQuY3N2KCJEOlxcREtFXFxUaGVzaXNfcmVsYXRlZFxcSW1wbGVtZW50YXRpb25cXEh5cGVyX3R1bmluZ19yZXN1bHRzXFxGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50LmNzdiIpDQoNCiNDb252ZXJ0IENsdXN0ZXIgSUQgZnJvbSBpbnQgdG8gZmFjdG9yDQpGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50PC0gRlMxX1QwX0NsdXN0ZXJfQXNzaWdubWVudCAlPiUgbXV0YXRlKENsdXN0ZXJfSUQ9YXMuZmFjdG9yKENsdXN0ZXJfSUQpKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgICAgICAgU2xpdHRpbmcgRGF0YSAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQpzZXQuc2VlZCgxMjMpDQojaWtlYV9zcGxpdA0KRlMxX1QwX0NsdXN0ZXJfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50LCBzdHJhdGEgPSBDbHVzdGVyX0lEKQ0KRlMxX1QwX0NsdXN0ZXJfdHJhaW4gPC0gdHJhaW5pbmcoRlMxX1QwX0NsdXN0ZXJfc3BsaXQpDQpGUzFfVDBfQ2x1c3Rlcl90ZXN0IDwtIHRlc3RpbmcoRlMxX1QwX0NsdXN0ZXJfc3BsaXQpDQoNCnNldC5zZWVkKDIzNCkNCkZTMV9UMF9DbHVzdGVyX2ZvbGRzIDwtIGJvb3RzdHJhcHMoRlMxX1QwX0NsdXN0ZXJfdHJhaW4sIHN0cmF0YSA9IENsdXN0ZXJfSUQpDQpGUzFfVDBfQ2x1c3Rlcl9mb2xkcw0KYGBgDQoNCg0KYGBge3J9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiMgdXNlX3JhbmdlciA6IHdpbGwgZ2l2ZSB3b3JrZmxvdyBwaXBlbGluZSBkZXRhaWxzICAgICAgICMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiNsaWJyYXJ5KHVzZW1vZGVscykNCnVzZV9yYW5nZXIoQ2x1c3Rlcl9JRCB+IC4sIGRhdGEgPSBGUzFfVDBfQ2x1c3Rlcl90cmFpbikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgICAgICAgIFN0YXJ0IHR1bmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KcmFuZ2VyX3JlY2lwZSA8LSANCiAgcmVjaXBlKGZvcm11bGEgPSBDbHVzdGVyX0lEIH4gLiwgZGF0YSA9IEZTMV9UMF9DbHVzdGVyX3RyYWluKSAlPiUgdXBkYXRlX3JvbGUoSW5zdGFuY2VfSUQsIG5ld19yb2xlID0gIklEIikgDQoNCnJhbmdlcl9zcGVjIDwtIA0KICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCBtaW5fbiA9IHR1bmUoKSwgdHJlZXMgPSAxMDAwKSAlPiUgDQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JSANCiAgc2V0X2VuZ2luZSgicmFuZ2VyIikgDQoNCnJhbmdlcl93b3JrZmxvdyA8LSANCiAgd29ya2Zsb3coKSAlPiUgDQogIGFkZF9yZWNpcGUocmFuZ2VyX3JlY2lwZSkgJT4lIA0KICBhZGRfbW9kZWwocmFuZ2VyX3NwZWMpIA0KDQpyYW5nZXJfZm9sZHMgPC0gdmZvbGRfY3YoRlMxX1QwX0NsdXN0ZXJfdHJhaW4pDQpzZXQuc2VlZCg2ODMyMikNCnJhbmdlcl90dW5lIDwtDQogIHR1bmVfZ3JpZChyYW5nZXJfd29ya2Zsb3csIHJlc2FtcGxlcyA9IHJhbmdlcl9mb2xkcywgZ3JpZCA9IDIwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiMgICAgVHVuZWQgcmVzdWx0cyBhbmQgdmFyaW91cyBtZXRyaWNzICAgICAgICAgICAgICAgICAgICMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCnJhbmdlcl90dW5lICU+JSBjb2xsZWN0X21ldHJpY3MoKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgZ3JhcGhpY2FsIHJlc3VsdHMgICAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnJhbmdlcl90dW5lICU+JQ0KICBjb2xsZWN0X21ldHJpY3MoKSAlPiUNCiAgZmlsdGVyKC5tZXRyaWMgPT0gInJvY19hdWMiKSAlPiUNCiAgbXV0YXRlKG1pbl9uID0gZmFjdG9yKG1pbl9uKSkgJT4lDQogIGdncGxvdChhZXMobXRyeSwgbWVhbiwgY29sb3IgPSBtaW5fbikpICsNCiAgZ2VvbV9saW5lKGFscGhhID0gMC41LCBzaXplID0gMS41KSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnMoeSA9ICJBVUMiKQ0KYGBgDQoNCg0KYGBge3J9DQojIFNwZWNpZnlpbmcgdGhlIG10cnkgYW5kIG1pbl9uIHJhbmdlIHRvIHVuZGVyc3RhbmQgYmV0dGVyDQpyZl9ncmlkX3JhbmdlciA8LSBncmlkX3JlZ3VsYXIoDQogIG10cnkocmFuZ2UgPSBjKDEwLCAzMCkpLA0KICBtaW5fbihyYW5nZSA9IGMoMiwgOCkpLA0KICBsZXZlbHMgPSA1DQopDQoNCnJmX2dyaWRfcmFuZ2VyDQoNCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbCgpDQoNCnJhbmdlcl90dW5lIDwtDQogIHR1bmVfZ3JpZChyYW5nZXJfd29ya2Zsb3csIHJlc2FtcGxlcyA9IHJhbmdlcl9mb2xkcywgZ3JpZCA9IHJmX2dyaWRfcmFuZ2VyKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgIFNob3cgYW5kIHNlbGVjdCB0aGUgYmVzdCAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnNob3dfYmVzdChyYW5nZXJfdHVuZSwgbWV0cmljID0gInJvY19hdWMiKQ0Kc2hvd19iZXN0KHJhbmdlcl90dW5lLCBtZXRyaWMgPSAiYWNjdXJhY3kiKQ0KDQpzZWxlY3RfYmVzdChyYW5nZXJfdHVuZSxtZXRyaWMgPSAicm9jX2F1YyIpDQphdXRvcGxvdChyYW5nZXJfdHVuZSkNCg0KYGBgDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgIGdyYXBoaWNhbCByZXN1bHRzICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQpyYW5nZXJfdHVuZSAlPiUNCiAgY29sbGVjdF9tZXRyaWNzKCkgJT4lDQogIGZpbHRlcigubWV0cmljID09ICJyb2NfYXVjIikgJT4lDQogIG11dGF0ZShtaW5fbiA9IGZhY3RvcihtaW5fbikpICU+JQ0KICBnZ3Bsb3QoYWVzKG10cnksIG1lYW4sIGNvbG9yID0gbWluX24pKSArDQogIGdlb21fbGluZShhbHBoYSA9IDAuNSwgc2l6ZSA9IDEuNSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHkgPSAiQVVDIikNCmBgYA0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgIFNob3cgYW5kIHNlbGVjdCB0aGUgYmVzdCAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnNob3dfYmVzdChyYW5nZXJfdHVuZSwgbWV0cmljID0gInJvY19hdWMiKQ0Kc2hvd19iZXN0KHJhbmdlcl90dW5lLCBtZXRyaWMgPSAiYWNjdXJhY3kiKQ0KDQpzZWxlY3RfYmVzdChyYW5nZXJfdHVuZSxtZXRyaWMgPSAicm9jX2F1YyIpDQphdXRvcGxvdChyYW5nZXJfdHVuZSkNCmBgYA0KDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICBmaW5hbGl6ZSB0aGUgbW9kZWwgYW5kIHdvcmtmbG93ICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQpmaW5hbF9yZiA8LSByYW5nZXJfd29ya2Zsb3cgJT4lDQogIGZpbmFsaXplX3dvcmtmbG93KHNlbGVjdF9iZXN0KHJhbmdlcl90dW5lKSkNCg0KZmluYWxfcmYNCmBgYA0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyBmaXQgdGhlIGJlc3QgdHVuZWQgcGFyYW1ldGVyIG9uIHRoZSB0cmFpbmluZyBkYXRhICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KRlMxX1QwX0NsdXN0ZXJfZml0IDwtIGxhc3RfZml0KGZpbmFsX3JmLCBGUzFfVDBfQ2x1c3Rlcl9zcGxpdCkNCkZTMV9UMF9DbHVzdGVyX2ZpdA0KDQpgYGANCg0KDQpgYGB7cn0NCmNvbGxlY3RfbWV0cmljcyhGUzFfVDBfQ2x1c3Rlcl9maXQpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHZpcCkNCg0KaW1wX3NwZWMgPC0gcmFuZ2VyX3NwZWMgJT4lDQogIGZpbmFsaXplX21vZGVsKHNlbGVjdF9iZXN0KHJhbmdlcl90dW5lKSkgJT4lDQogIHNldF9lbmdpbmUoInJhbmdlciIsIGltcG9ydGFuY2UgPSAicGVybXV0YXRpb24iKQ0KDQp3b3JrZmxvdygpICU+JQ0KICBhZGRfcmVjaXBlKHJhbmdlcl9yZWNpcGUpICU+JQ0KICBhZGRfbW9kZWwoaW1wX3NwZWMpICU+JQ0KICBmaXQoRlMxX1QwX0NsdXN0ZXJfdHJhaW4pICU+JQ0KICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JQ0KICB2aXAoYWVzdGhldGljcyA9IGxpc3QoYWxwaGEgPSAwLjgsIGZpbGwgPSAibWlkbmlnaHRibHVlIikpDQpgYGANCg0KDQotLS0NCiMgUmFuZ2VyIHBhY2thZ2UgDQoqIEFib3ZlIHR1bmVkIHZhbHVlcyB1c2VkIGZvciByYW5nZXIgbW9kZWwuIDwvYnI+DQoqIFJhbmdlciBpcyBhIGZhc3QgaW1wbGVtZW50YXRpb24gb2YgcmFuZG9tIGZvcmVzdHMgKEJyZWltYW4gMjAwMSkgb3IgcmVjdXJzaXZlIHBhcnRpdGlvbmluZywgcGFydGljdWxhcmx5IHN1aXRlZCBmb3IgaGlnaCBkaW1lbnNpb25hbCBkYXRhLiA8L2JyPiANCg0KYGBge3J9DQp0aWR5X3Jhbmdlcl9tb2RlbCA8LSByYW5nZXIoQ2x1c3Rlcl9JRCB+IC4sIGRhdGEgPSBGUzFfVDBfQ2x1c3Rlcl90cmFpblssLTFdICwgaW1wb3J0YW5jZSA9ICJwZXJtdXRhdGlvbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsLmltcG9ydGFuY2UgPSBUUlVFLG10cnkgPSAxMCxudW0udHJlZXMgPSAxMDAwLGNsYXNzaWZpY2F0aW9uID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ubm9kZS5zaXplID0gMikNCmBgYA0KDQpgYGB7cn0NCnRpZHlfcmFuZ2VyX21vZGVsDQpgYGANCg0KDQojIFRyYWluIGFjY3VyYWN5DQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHRpZHlfcmFuZ2VyX21vZGVsJGNvbmZ1c2lvbi5tYXRyaXgpDQpgYGANCg0KIyBUZXN0IGFjY3VyYWN5DQpgYGB7cn0NCnRpZHlfcmFuZ2VyX3ByZWQuZGF0YSA8LSBwcmVkaWN0KHRpZHlfcmFuZ2VyX21vZGVsLCBkYXRhID0gRlMxX1QwX0NsdXN0ZXJfdGVzdCkNCnRhYmxlKEZTMV9UMF9DbHVzdGVyX3Rlc3QkQ2x1c3Rlcl9JRCwgdGlkeV9yYW5nZXJfcHJlZC5kYXRhJHByZWRpY3Rpb25zKQ0KDQpgYGANCg0KYGBge3J9DQp0aWR5X3Jhbmdlcl9jbTwtY29uZnVzaW9uTWF0cml4KHRhYmxlKEZTMV9UMF9DbHVzdGVyX3Rlc3QkQ2x1c3Rlcl9JRCwgdGlkeV9yYW5nZXJfcHJlZC5kYXRhJHByZWRpY3Rpb25zLCBkbm4gPSBjKCJSZWZlcmVuY2UiLCAiUHJlZGljdGlvbiIpKSkNCnRpZHlfcmFuZ2VyX2NtDQp0aWR5KHRpZHlfcmFuZ2VyX2NtKQ0KYGBgDQoNCg0KIyBDb25jbHVzaW9uIDogTW9kZWwgYWNjdXJhY3kNCg0KKiBUcmFpbiBBY2N1cmFjeSA6IDczLjY2JSA8L2JyPg0KKiBUZXN0IEFjY3VyYWN5IDogNzAuMDIlIDwvYnI+DQoqIEJvdGggYXJlIGNsb3NlIGJ5LiBCYWxhbmNlZCBtb2RlbC4gPC9icj4=